﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Poligono.Core;
using System.Threading;
using System.Data.Common;
using System.Configuration;

namespace Poligono.Data
{
    /// <summary>
    /// Provider to keep acess to a DataBase 
    /// </summary>
    public class DataAcessProvider : DisposeControl
    {
        #region Members

        private string _ConnectionStringSettingName;
        private DbTransaction _Transaction;
        private DbProviderFactory _ProviderFactory;
        private DbConnection _Connection;
        #endregion

        #region Property

        /// <summary>
        /// Get connectionString name, that value is set at App.config. See too <see cref="ConnectionStringName"/> 
        /// </summary>
        public string ConnectionStringSettingName
        {
            get
            {
                CheckDispose();//dont return when diposed
                return _ConnectionStringSettingName;
            }
            internal set
            {
                _ConnectionStringSettingName = value;
            } 
        }

        /// <summary>
        /// Connection maked by factory
        /// </summary>
        public DbConnection Connection
        {
            get
            {
                CheckDispose();
                if (_Connection == null && _ProviderFactory != null)
                {
                    _Connection = ProviderFactory.CreateConnection();
                }
                return _Connection;
            }
            set
            {
                CheckDispose();
                ConnectionStringSettingName = string.Empty;
                _Connection = value;
            }
        }

        /// <summary>
        /// Provider Factory create to connect with DataBase
        /// </summary>
        public DbProviderFactory ProviderFactory
        {
            get
            {
                CheckDispose();
                return _ProviderFactory;
            }
            set
            {
                CheckDispose();
                _ProviderFactory = value;
            }
        }

        /// <summary>
        /// Transaction in use by connection
        /// </summary>
        public DbTransaction Transaction
        {
            get
            {
                CheckDispose();
                return _Transaction;
            }
            set
            {
                CheckDispose();
                _Transaction = value;
            }
        }

        #endregion

        #region Constructor

        #region Constructor with app.config

        /// <summary>
        /// Constructor basic, only will be open a connection with Default BD <see cref="ConnectionStringName"/>
        /// </summary>
        public DataAcessProvider()
            : this(ConnectionStringName.Default)
        {
        }

        /// <summary>
        /// Constructor to config connection with provider/container, using connectionString at app.config
        /// </summary>
        /// <param name="connectionStringSettingName">Connection String name,<see cref="ConnectionStringName"/>
        /// <remarks>value has need set at app.config</remarks>
        /// </param>
        public DataAcessProvider(ConnectionStringName connectionStringSettingName)
            : this(connectionStringSettingName.ToString())
        {
        }
        /// <summary>
        /// Constructor to config connection with provider/container, using connectionString at app.config
        /// </summary>
        /// <param name="connectionStringSettingName">Connection String name,<see cref="ConnectionStringName"/>
        /// <remarks>value has need set at app.config</remarks>
        /// </param>
        public DataAcessProvider(string connectionStringSettingName)
            : this(ConfigurationManager.ConnectionStrings[connectionStringSettingName])
        {
        }

        internal DataAcessProvider(ConnectionStringSettings connectionStringSettings) : this(connectionStringSettings.ProviderName, connectionStringSettings.ConnectionString)
        {

        }

        /*
        /// <summary>
        /// Constructor to config connection with provider/container
        /// </summary>
        /// <param name="providerFactory">Provider Factory, <see cref="DbProviderFactories"/> </param>
        /// <param name="connectionStringSettingName">Connection string name</param>
        public DataAcessProvider(DbProviderFactory providerFactory, ConnectionString connectionStringSettingName)
            : this(providerFactory, connectionStringSettingName.ToString())
        {
        }

        /// <summary>
        /// Constructor to config connection with provider/container
        /// </summary>
        /// <param name="providerName">Provider name, <see cref="DbProviderFactories"/> </param>
        /// <param name="connectionStringSettingName">Connection string name</param>
        public DataAcessProvider(string providerName, ConnectionString connectionStringSettingName)
            : this(DbProviderFactories.GetFactory(providerName), connectionStringSettingName.ToString())
        {
        }

        /// <summary>
        /// Constructor to config connection with provider/container, using connectionString at app.config
        /// </summary>
        /// <param name="connectionStringSettingName">Connection String name,<see cref="ConnectionString"/>
        /// <remarks>value has need set at app.config</remarks>
        /// </param>
        public DataAcessProvider(DbProviderFactory providerFactory, string connectionStringSettingName)
        {
            ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings[connectionStringSettingName];            
            if (connectionStringSettings == null)
            {
                throw new InvalidOperationException("not legal at <configuration><connectionString>");
            }
            commonConstructor(providerFactory, connectionStringSettings.ConnectionString);
            ConnectionStringSettingName = connectionStringSettings.Name;
        }        
        */
        #endregion


        /// <summary>
        /// Constructor to config connection with provider/container
        /// </summary>
        /// <param name="providerName">Provider name, <see cref="DbProviderFactories"/> </param>
        /// <param name="connectionString">Connection string</param>
        public DataAcessProvider(string providerName, string connectionString)
            : this(DbProviderFactories.GetFactory(providerName), connectionString)
        {
        }

                
        
        /// <summary>
        /// Constructor to config connection with provider/container
        /// </summary>
        /// <param name="providerFactory">Provider Factory, <see cref="DbProviderFactories"/> </param>
        /// <param name="connectionString">Connection string</param>
        public DataAcessProvider(DbProviderFactory providerFactory, string connectionString)
        {
            commonConstructor(providerFactory, connectionString);
        }
        
        private void commonConstructor(DbProviderFactory providerFactory, string connectionString)
        {
            if (providerFactory == null || string.IsNullOrEmpty(connectionString))
            {
                throw new InvalidOperationException("ProviderFactory and ConnectionString need has a value");
            }
            ProviderFactory = providerFactory;
            ConnectionStringSettingName = string.Empty;
            Connection = ProviderFactory.CreateConnection();
            Connection.ConnectionString = connectionString;
        }
        #endregion

        /// <summary>
        /// Method Disposing
        /// </summary>
        /// <param name="disposing">mark of disposing called by finalize</param>
        protected override void Dispose(bool disposing)
        {
            if (IsDisposed && disposing)
            {
                try
                {
                    if (Transaction != null)
                        Transaction.Dispose();
                    
                }
                catch (Exception e)
                {
                    throw e;
                }
                finally
                {
                    if (_Connection != null && _Connection.State == System.Data.ConnectionState.Open)
                        _Connection.Close();
                    _Connection.Dispose();
                }
            }
            base.Dispose(disposing);
        }


        /// <summary>
        /// Menssager of erro
        /// </summary>
        protected override string MensagemErroObjectDisposedException
        {
            get
            {
                return string.Format("ScopeID: {0} Identity: {0}",
                 Thread.CurrentThread.ManagedThreadId
                 , Thread.CurrentPrincipal.Identity.Name);
            }
        }
    }
}
